package com.tosmart.jorm.face;

import com.tosmart.jorm.impl.JormImpl;
import com.tosmart.jorm.impl.JormReflect;

import java.sql.Connection;
import java.util.List;
import java.util.Map;

/**
 * JORM framework such as the entrance, all the
 * user actions by such access.
 * <p/>
 * Such a framework provides a JORM set of function
 * calls approach, including the Entity of the
 * insert, update, delete, search and other functions.
 * <p/>
 * Point in need of special note are:
 * <p/>
 * In order to allow JORM frame work, the database
 * connection is only required for the framework of
 * the offer.
 * <p/>
 * In the framework of the whole work cycle, the
 * framework is not responsible for the life-cycle
 * maintenance of the database connection.
 *
 * @author tosmart@gmail.com
 * @see com.tosmart.jorm.impl.JormReflect
 * @see com.tosmart.jorm.impl.SqlBuilder
 */
public class JormFace {

    /**
     * Constructor method, introduced a database
     * connection as a parameter in the entire life
     * cycle of OrmEntry class, the database connection
     * will be used multiple times.
     * <p/>
     * As JORM framework does not interfere with the
     * maintenance of the database connection, external
     * incoming database connection established, close
     * the operational need for external call themselves
     * realize.
     *
     * @param connection Database connection
     */
    public JormFace(Connection connection) {
        this.connection = connection;
    }

    /**
     * Based on the supplied Map objects and Entity
     * classes, automatically the value of Map objects
     * as a collection of objects to generate Entity
     * classes will carry an object, and then return it.
     *
     * @param entityClass Entity Class(marks with the @JormEntity)
     * @param map         Map is usually the ResultSet
     *                    from the database query results
     *                    come in, a Map of packaging a
     *                    database record.
     *                    The Key of the Map is database
     *                    table column names, ant the Value
     *                    of the Map is object value.
     * @param <T>         Entity Class
     * @return the new Entity instance, values of the
     *         instance are ready
     * @see com.tosmart.jorm.impl.JormImpl#createEntity(Class, java.util.Map)
     */
    public <T> T createEntity(
            Class<T> entityClass, Map<String, Object> map) {

        JormReflect.validEntityClass(entityClass);

        return new JormImpl(connection).createEntity(
                entityClass,
                map
        );
    }

    /**
     * JORM load an Entity instance of the framework,
     * we need to use parameters to specify the
     * primary key value for this Entity instance.
     * <p/>
     * There is only one primary key for the database
     * tables, primary key value is a single, uniquely
     * determine the value of an Entity instance,
     * which can be an integer, a string, etc.
     * <p/>
     * For there are two or more database tables
     * primary key, JORM framework, agreement, direct
     * the use of an Entity instance as the primary
     * key value, this instance has been assigned
     * primary key values of the corresponding fields.
     * <p/>
     * if not found the entity where the primary key
     * value, return null
     * <p/>
     * If the Entity class contains @JormRefer
     * or @JormReferList field, this method will be
     * automatically linked to map object(s).
     *
     * @param entityClass Entity class
     * @param entityId    primary key value, a Entity
     *                    instance include the values
     *                    of the primary keys is required
     *                    if Entity has multiple primary
     *                    keys.
     * @param <T>         Entity class
     * @return the result of the search by Entity primary
     *         key(s) value, null if not found
     */
    public <T> T loadEntity(
            Class<T> entityClass, Object entityId) {

        JormReflect.validEntityClass(entityClass);

        return new JormImpl(connection).loadEntity(
                entityClass,
                entityId
        );
    }

    /**
     * Insert a new Entity instance into the database.
     * <p/>
     * If the Entity instance contains an automatic
     * increase in the number fields, and there is no
     * value assignment, JORM framework will provide
     * automatic increase in value to it.
     *
     * @param entity Entity instance
     * @return the number of records affected in database.
     */
    public int insertEntity(Object entity) {

        JormReflect.validEntityClass(entity.getClass());

        return new JormImpl(connection).insertEntity(
                entity
        );
    }

    /**
     * Delete database records associated with Entity instance.
     *
     * @param entity Entity instance
     * @return the number of records affected in database.
     */
    public int deleteEntity(Object entity) {

        JormReflect.validEntityClass(entity.getClass());

        return new JormImpl(connection).deleteEntity(
                entity
        );
    }

    /**
     * Update database records associated with Entity instance.
     *
     * @param entity Entity instance
     * @return the number of records affected in database.
     */
    public int updateEntity(Object entity) {

        JormReflect.validEntityClass(entity.getClass());

        return new JormImpl(connection).updateEntity(
                entity
        );
    }

    /**
     * List all the Entity instances of the associated
     * database table records.
     * <p/>
     * If the database is not associated with the record
     * exists, returns an empty list.
     * <p/>
     * If the Entity class contains @JormRefer or @JormReferList
     * field, the instance in the list will be automatically
     * linked to map object(s).
     *
     * @param entityClass Entity class
     * @param <T>         Entity class
     * @return Entity instances list
     */
    public <T> List<T> listEntities(Class<T> entityClass) {

        JormReflect.validEntityClass(entityClass);

        return new JormImpl(connection).listEntities(
                entityClass
        );
    }

    /**
     * According to arguments provided by the Entity
     * class, as well as the Entity corresponding
     * database table column names in the list, with
     * their corresponding values, search the database
     * of eligible records.
     *
     * @param entityClass Entity class
     * @param columns     the name of the columns in
     *                    database table
     * @param values      the values of the columns as condition
     * @param <T>         Entity class
     * @return Entity instances list
     */
    public <T> List<T> find(
            Class<T> entityClass,
            String[] columns, Object[] values) {

        JormReflect.validEntityClass(entityClass);

        return new JormImpl(connection).find(
                entityClass,
                columns,
                values
        );
    }

    /**
     * Additional extension of SQL-based queries,
     * typically used such as follows:
     * <code>
     * findEx(
     * Person.class,
     * "where name like ?",
     * "%jim%"
     * );
     * </code>
     * <p/>
     * return an empty list if not found
     *
     * @param entityClass Entity class
     * @param addClause   Additional query clause of SQL
     *                    statement, use? Placeholder
     *                    parameter values
     * @param params      Additional query clause used in the
     *                    query list of values.
     * @param <T>         Entity class
     * @return Entity instances list
     */
    public <T> List<T> findEx(
            Class<T> entityClass, String addClause,
            Object... params) {

        JormReflect.validEntityClass(entityClass);

        return new JormImpl(connection).findEx(
                entityClass,
                addClause,
                params
        );
    }

    /**
     * Example finding same to Hibernate, with additional condition.
     *
     * @param entity           Entity instance
     * @param selector         the field filter interface
     *                         implements of users.
     * @param enableStringLike use SQL 'like' express for string value.
     * @param addClause        Additional query clause of SQL
     *                         statement, use? Placeholder
     *                         parameter values
     * @param addParams        Additional query clause used in the
     *                         query list of values.
     * @param <T>              Entity class
     * @return Entity instances list, return empty list if no record.
     */
    public <T> List<T> exampleFind(
            T entity, ColumnSelector selector,
            boolean enableStringLike,
            String addClause, Object... addParams) {

        JormReflect.validEntityClass(entity.getClass());

        return new JormImpl(connection).exampleFind(
                entity,
                selector,
                enableStringLike,
                addClause,
                addParams
        );
    }

    /**
     * Example finding same to Hibernate.
     *
     * @param entity           Entity instance
     * @param selector         the field filter interface
     *                         implements of users.
     * @param enableStringLike use SQL 'like' express for string value.
     * @param <T>              Entity class
     * @return Entity instances list, return empty list if no record.
     */
    public <T> List<T> exampleFind(
            T entity, boolean enableStringLike,
            ColumnSelector selector) {

        JormReflect.validEntityClass(entity.getClass());

        return new JormImpl(connection).exampleFind(
                entity,
                selector,
                enableStringLike,
                ""
        );
    }

    /**
     * The setter of the Database connection.
     *
     * @param connection the new Connection instance
     */
    public void setConnection(Connection connection) {
        this.connection = connection;
    }

    private Connection connection;
}
